import socket
import threading
import os
import time
from PyQt6.QtWidgets import QMessageBox
from PyQt6.QtCore import QObject, pyqtSignal
import subprocess

class NetworkHandler(QObject):
    # 添加信号
    progress_updated = pyqtSignal(int)  # 传输进度信号
    transfer_completed = pyqtSignal(str)  # 传输完成信号
    transfer_error = pyqtSignal(str)  # 传输错误信号
    
    def __init__(self, udp_port=5000, tcp_port=5001):
        super().__init__()
        self.udp_port = udp_port
        self.tcp_port = tcp_port
        self.buffer_size = 4096
        
        # 启动UDP监听线程
        self.udp_thread = threading.Thread(target=self._start_udp_listener)
        self.udp_thread.daemon = True
        self.udp_thread.start()
        
        # 启动TCP监听线程
        self.tcp_thread = threading.Thread(target=self._start_tcp_listener)
        self.tcp_thread.daemon = True
        self.tcp_thread.start()
        
    def _get_valid_interfaces(self):
        """获取有效的网络接口IP地址（排除虚拟机和无效接口）"""
        valid_ips = []
        try:
            # 创建临时UDP socket来获取本机IP
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            try:
                # 不真正发送数据，只是获取本机IP
                s.connect(('8.8.8.8', 1))
                valid_ips.append(s.getsockname()[0])
            except:
                pass
            finally:
                s.close()
                
            # 获取所有IP，但排除特殊IP
            hostname = socket.gethostname()
            all_ips = socket.gethostbyname_ex(hostname)[2]
            
            for ip in all_ips:
                # 排除本地回环、虚拟机和特殊IP段
                if (not ip.startswith(('127.', '169.254.', '192.168.56.', '192.168.99.', '172.16.', '10.')) and
                    ip not in valid_ips):
                    valid_ips.append(ip)
                    
        except Exception as e:
            print(f"获取网络接口时出错: {str(e)}")
            
        return valid_ips

    def scan_network(self, device_name=""):
        """广播UDP消息来扫描网络设备"""
        devices = []
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        
        try:
            # 获取有效的网络接口
            valid_ips = self._get_valid_interfaces()
            if not valid_ips:
                print("未找到有效的网络接口")
                return devices
                
            # 对每个有效接口发送广播
            for ip in valid_ips:
                try:
                    # 构造广播地址
                    broadcast_ip = '.'.join(ip.split('.')[:-1] + ['255'])
                    message = f"SCAN:{device_name}"
                    print(f"发送广播到 {broadcast_ip}")
                    sock.sendto(message.encode(), (broadcast_ip, self.udp_port))
                except Exception as e:
                    print(f"发送广播到 {ip} 时出错: {str(e)}")
                    
            # 等待响应
            sock.settimeout(2)
            start_time = time.time()
            
            while time.time() - start_time < 2:  # 等待2秒
                try:
                    data, addr = sock.recvfrom(1024)
                    response = data.decode()
                    if response.startswith("AVAILABLE:"):
                        remote_name = response[len("AVAILABLE:"):]
                        remote_ip = addr[0]
                        
                        # 检查是否是虚拟机IP
                        if not any(remote_ip.startswith(prefix) for prefix in 
                                 ['192.168.56.', '192.168.99.', '172.16.', '10.']):
                            # 避免重复添加
                            if not any(d['ip'] == remote_ip for d in devices):
                                devices.append({
                                    'name': remote_name or "未命名设备",
                                    'ip': remote_ip
                                })
                                print(f"发现设备: {remote_name} ({remote_ip})")
                except socket.timeout:
                    continue
                except Exception as e:
                    print(f"接收响应时出错: {str(e)}")
                    
        except Exception as e:
            print(f"扫描网络时出错: {str(e)}")
        finally:
            sock.close()
            
        return devices
        
    def _start_udp_listener(self):
        """监听UDP广播消息"""
        while True:
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                sock.bind(('', self.udp_port))
                
                while True:
                    try:
                        data, addr = sock.recvfrom(1024)
                        sender_ip = addr[0]
                        
                        # 检查是否来自虚拟机
                        if any(sender_ip.startswith(prefix) for prefix in 
                              ['192.168.56.', '192.168.99.', '172.16.', '10.']):
                            continue
                            
                        message = data.decode()
                        if message.startswith("SCAN:"):
                            # 从配置文件读取设备名称
                            try:
                                with open('device_name.txt', 'r') as f:
                                    device_name = f.read().strip()
                            except:
                                device_name = ""
                            
                            response = f"AVAILABLE:{device_name}"
                            sock.sendto(response.encode(), addr)
                            print(f"响应扫描请求: {sender_ip}")
                    except Exception as e:
                        print(f"处理UDP消息时出错: {str(e)}")
                        continue
                        
            except Exception as e:
                print(f"UDP监听器出错，准备重启: {str(e)}")
                try:
                    sock.close()
                except:
                    pass
                time.sleep(1)
                continue
                
    def _start_tcp_listener(self):
        """监听TCP文件传输连接"""
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('', self.tcp_port))
        sock.listen(5)
        
        while True:
            try:
                client, addr = sock.accept()
                threading.Thread(target=self._handle_file_receive,
                              args=(client,)).start()
            except:
                continue
                
    def _handle_file_receive(self, client):
        """处理接收文件"""
        try:
            # 接收文件名，使用 GBK 编码
            try:
                file_name = client.recv(1024).decode('gbk')
            except UnicodeDecodeError:
                file_name = client.recv(1024).decode('utf-8', errors='ignore')
            
            # 接收文件大小
            file_size = int(client.recv(1024).decode('ascii'))
            
            # 确保文件名合法
            file_name = "".join(c for c in file_name if c.isprintable())
            
            # 创建保存文件的完整路径
            save_dir = os.path.join(os.path.expanduser("~"), "Downloads", "FileShare")
            os.makedirs(save_dir, exist_ok=True)
            save_path = os.path.join(save_dir, f"received_{file_name}")
            
            # 接收文件内容
            received_size = 0
            with open(save_path, 'wb') as f:
                while received_size < file_size:
                    data = client.recv(self.buffer_size)
                    f.write(data)
                    received_size += len(data)
                    # 发送进度信号
                    progress = int((received_size / file_size) * 100)
                    self.progress_updated.emit(progress)
            
            # 发送完成信号
            self.transfer_completed.emit(save_path)
            
        except Exception as e:
            print(f"接收文件时出错: {str(e)}")
        finally:
            client.close()
            
    def send_file(self, file_path, target_ip):
        """发送文件到指定IP"""
        # 在新线程中启动传输
        transfer_thread = threading.Thread(
            target=self._send_file_thread,
            args=(file_path, target_ip)
        )
        transfer_thread.daemon = True
        transfer_thread.start()
    
    def _send_file_thread(self, file_path, target_ip):
        """在单独的线程中处理文件发送"""
        try:
            # 验证IP地址
            try:
                socket.inet_aton(target_ip)
            except socket.error:
                self.transfer_error.emit(f"无效的IP地址: {target_ip}")
                return
            
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)  # 添加连接超时
            try:
                sock.connect((target_ip, self.tcp_port))
            except socket.timeout:
                self.transfer_error.emit(f"连接超时: {target_ip}")
                return
            except ConnectionRefusedError:
                self.transfer_error.emit(f"连接被拒绝: {target_ip}")
                return
            
            try:
                # 发送文件名
                file_name = os.path.basename(file_path)
                try:
                    sock.send(file_name.encode('gbk'))
                except UnicodeEncodeError:
                    sock.send(file_name.encode('utf-8'))
                
                time.sleep(0.1)
                
                # 发送文件大小
                file_size = os.path.getsize(file_path)
                sock.send(str(file_size).encode('ascii'))
                
                time.sleep(0.1)
                
                # 发送文件内容
                sent_size = 0
                with open(file_path, 'rb') as f:
                    while True:
                        data = f.read(self.buffer_size)
                        if not data:
                            break
                        sock.send(data)
                        sent_size += len(data)
                        # 发送进度信号
                        progress = int((sent_size / file_size) * 100)
                        self.progress_updated.emit(progress)
                
            except Exception as e:
                self.transfer_error.emit(f"传输过程出错: {str(e)}")
                
        except Exception as e:
            self.transfer_error.emit(f"发送文件时出错: {str(e)}")
        finally:
            try:
                sock.close()
            except:
                pass
            
    def open_file_location(self, file_path):
        """打开文件所在文件夹"""
        try:
            if os.name == 'nt':  # Windows
                subprocess.run(['explorer', '/select,', file_path])
            elif os.name == 'posix':  # macOS 和 Linux
                if os.system('which xdg-open') == 0:  # Linux
                    subprocess.run(['xdg-open', os.path.dirname(file_path)])
                else:  # macOS
                    subprocess.run(['open', '-R', file_path])
        except Exception as e:
            print(f"打开文件位置时出错: {str(e)}") 